package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.common.core.CollectionHelper;
import com.wiscamp.ninechapters.problems.domain.models.tasks.Task;
import com.wiscamp.ninechapters.problems.domain.repositories.TaskRepository;
import com.wiscamp.ninechapters.problems.infrastructure.converters.TaskConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.tasks.TaskMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.tasks.TaskViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.po.tasks.TaskPO;
import com.wiscamp.ninechapters.problems.infrastructure.po.tasks.TaskView;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
import java.util.Collection;
import java.util.Objects;

@Repository
public class TaskRepositoryImpl extends BaseRepository implements TaskRepository {

    private final TaskMapper taskMapper;

    private final TaskViewMapper taskViewMapper;

    public TaskRepositoryImpl(TaskMapper taskMapper, TaskViewMapper taskViewMapper) {
        this.taskMapper = taskMapper;
        this.taskViewMapper = taskViewMapper;
    }

    //region task
    private TaskPO getTaskPO(@Min(0) long taskId) {
        return PersistenceUtils.findById(taskId, taskMapper::findById);
    }

    @HandleDataException
    public Task getTask(@Min(0) long taskId) {
        return PersistenceUtils.findEntityById(taskId, taskViewMapper::findById,
                TaskConverter.Instance::toTaskByView);
    }

    @HandleDataException
    public Task getTask(@Min(0) long assignmentId, @Min(0) long detailId, @Min(0) long setId, @Min(0) long userId) {
        var taskView = taskViewMapper.findByAssignmentAndDetail(assignmentId, detailId, setId, userId);
        if (Objects.isNull(taskView)) return null;
        return TaskConverter.Instance.toTaskByView(taskView);
    }

    @HandleDataException
    public Task getLastTaskBySet(@Min(0) long setId, @Min(0) long userId, @Min(0) int taskStatusId) {
        var taskView = taskViewMapper.findFirstBySetIdAndUserIdAndTaskStatusId(setId, userId, taskStatusId);
        if (Objects.isNull(taskView)) return null;
        return TaskConverter.Instance.toTaskByView(taskView);
    }

    @HandleDataException
    public Collection<Task> getTasksBySet(long assignmentId, long setId) {
        return PersistenceUtils.getEntitiesByIdAndId(assignmentId, setId, taskViewMapper::findListByAssignmentAndSet,
                TaskConverter.Instance::toTaskByView);
    }


    @HandleDataException
    public PagedObjects<Task> getTasksByQuery(@NotNull PagedQuery query) {
        PagedObjects<TaskView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, taskViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, TaskConverter.Instance::toTaskByView);
    }

    @HandleDataException
    public Collection<Task> getTasksByUserAndStatus(@Min(0) long userId, int statusId) {
        var taskViews = taskViewMapper.findListByUserAndStatus(userId, statusId);
        return CollectionHelper.convertToArrayList(taskViews, TaskConverter.Instance::toTaskByView);
    }

    @HandleDataException
    public Collection<Task> getPagedTasksByUserAndStatus(@Min(0) long userId, @Min(0) int pageNumber, int statusId) {
        var sort = Sort.by(Sort.Direction.DESC, "taskId");
        var pageable = PageRequest.of(pageNumber - 1, 30, sort);
        Page<TaskView> pagedViews = taskViewMapper.findPagedTasksByUserAndStatus(userId, statusId, pageable);
        var taskViews = pagedViews.getContent();
        return CollectionHelper.convertToArrayList(taskViews, TaskConverter.Instance::toTaskByView);
    }

    @HandleDataException
    public int getTaskQuantityByDate(LocalDate datetimeStart, LocalDate datetimeEnd) {
        return taskViewMapper.findTaskQuantityByDate(datetimeStart, datetimeEnd);
    }


    @HandleDataException
    public Task getFirstTask(long setId, long userId, int taskStatus) {
        var taskView = taskViewMapper.findFirstBySetIdAndUserIdAndTaskStatusId(setId, userId, taskStatus);
        if (Objects.isNull(taskView)) return null;
        return TaskConverter.Instance.toTaskByView(taskView);
    }

    @HandleDataException
    public int getUserTasksQuantity(long userId) {
        return taskViewMapper.findTaskQuantityByUser(userId);
    }

    @HandleDataException
    public int getUserTaskQuantityByStatus(long userId, int statusId) {
        return taskViewMapper.findTaskQuantityByUserAndStatus(userId, statusId);
    }

    @HandleDataException
    public Task saveTask(@NotNull Task task) {
        // save assignment, assign to students by default
        long newId = 0;
        if (task.getTaskId() <= 0) {
            var taskPO = TaskConverter.Instance.toTaskPO(task);
            var newTaskPO = taskMapper.saveAndFlush(taskPO);
            newId = newTaskPO.getTaskId();
        } else {
            var existedPO = getTaskPO(task.getTaskId());
            var taskPO = TaskConverter.Instance.toTaskPO(task, existedPO);
            var newTaskPO = taskMapper.saveAndFlush(taskPO);
            newId = newTaskPO.getTaskId();
        }
        // save details
        return getTask(newId);
    }

    @HandleDataException
    public boolean removeTasksByStatus(long assignmentId, long setId, int taskStatus, Collection<Long> userIds) {
        taskMapper.removeUserTasksByStatus(assignmentId, setId, taskStatus, userIds);
        return true;
    }

    @HandleDataException
    public boolean updateTasksStatus(int uncompletedStatus, int abandonedStatus, String startDate, String endDate) {
        taskMapper.updateTasksStatus(uncompletedStatus, abandonedStatus, startDate, endDate);
        return true;
    }

    @HandleDataException
    public void deleteAbandonTasks(int abandonedStatus, String startDate, String endDate) {
        //assignmentTaskMapper.deleteAbandonTasks(abandonedStatus, startDate, endDate);
    }


    /*@HandleDataException
    public ExaminationCode getExaminationCode(String examinationCode) {
        var po = examinationCodeMapper.findExaminationCode(examinationCode);
        if (Objects.nonNull(po))
            return toExaminationCode(po);
        return null;
    }*/
    //endregion

}
